package per.sunmes.lesrb.manager;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.FileHandleResolver;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;

import per.sunmes.lesrb.util.GM;

/**
 * ResourceManager
 */
public class RM {
	/**
	 * RES dir path.
	 */
	public static String RES_DIR = "resource";
	public static boolean isGetFileCheckAbsolut = true;
	public static boolean isGetFileCheckInternal = true;
	public static boolean isGetFileCheckExternal = true;
	public static boolean isGetFileCheckResDirExternal = true;

	private static RM i;

	private static final String TAG = "ResourceManager";

	public static RM I() {
		if (i == null) {
			i = new RM();
		}
		return i;
	}

	public static void dispose() {
		if (i != null) {
			i._dispose();
			i = null;
		}
	}

	public static FileHandle getFile(String filePath) {
		return I()._getFile(filePath);
	}

	public static AssetManager getAsset() {
		return I().asset;
	}

	public static FileHandleResolver getFileHandleResolver() {
		return I().resolver;
	}

	public static void addDisposable(Disposable dp) {
		Set<Disposable> set = I().disposables;
		if (!set.contains(dp)) {
			set.add(dp);
		}
	}

	public static void addDisposable(String name, Disposable dp) throws RuntimeException {
		Map<String, Disposable> cache = I().cachedDisposables;
		if (cache.containsKey(name)) {
			throw new RuntimeException("A Disposable cache already used name :" + name);
		} else {
			cache.put(name, dp);
		}
	}

	/**
	 * if name was cached, old one will disposed.
	 * 
	 * @param name
	 * @param dp
	 */
	public static void addDisposableReplace(String name, Disposable dp) {
		Map<String, Disposable> cache = I().cachedDisposables;
		if (cache.containsKey(name)) {
			Disposable odp = cache.get(name);
			if (odp != null && odp != dp) {
				odp.dispose();
			}
		}
		cache.put(name, dp);
	}

	public static Disposable getCache(String name) {
		return I().cachedDisposables.get(name);
	}

	public static Texture getWhiteRectTexture() {
		return I()._getWhiteRect();
	}

	public static TextureRegion getRegion(String name) {
		return I()._loadGetEverywhere(name);
	}

	public static Drawable getCachedDrawable(String name) {
		return I()._getDrawable(name);
	}

	public static Drawable getMaskDrawable() {
		return I()._getMaskDrawable();
	}

	public static TextureRegion getRegionAtAtlas(String regionName, String atlasName) {
		return I()._loadTextureRegionFromAtlas(regionName, atlasName);
	}

	public static Texture getTexture(String name) {
		return I()._loadGetTextureFromFile(name);
	}

	public static void assetLoadRecordAtlas(String atlasName) {
		I()._loadAtlasAndCache(atlasName);
	}

	public static Map<String, TextureRegion> getRegionCache() {
		return I().regionsCache;
	}

	public static Array<String> getLoadedAtlasNames() {
		return I().loadedAtlasNames;
	}

	////////////////
	AssetManager asset;
	FileHandleResolver resolver;
	Texture whiteRectTexture;

	Map<String, Disposable> cachedDisposables = new HashMap<String, Disposable>();
	Set<Disposable> disposables = new HashSet<Disposable>();
	Map<String, TextureRegion> regionsCache = new HashMap<String, TextureRegion>();
	Map<String, Drawable> drawableCache = new HashMap<String, Drawable>();
	Drawable maskDrawable;

	Array<String> loadedAtlasNames = new Array<String>();

	private RM() {
		resolver = new FileHandleResolver() {
			public FileHandle resolve(String file) {
				return _getFile(file);
			}
		};
		asset = new AssetManager(resolver);

	}

	private Drawable _getDrawable(String name) {
		Drawable db = drawableCache.get(name);
		if (db == null) {
			db = new TextureRegionDrawable(_loadGetEverywhere(name));
			drawableCache.put(name, db);
		}
		return db;
	}

	private void _loadAtlasAndCache(String atlasName) {
		if (asset.isLoaded(atlasName, TextureAtlas.class)) {
			if (!loadedAtlasNames.contains(atlasName, false)) {
				loadedAtlasNames.add(atlasName);
			}
		} else {
			loadedAtlasNames.add(atlasName);
			asset.load(atlasName, TextureAtlas.class);
		}
	}

	private TextureRegion _loadGetEverywhere(String textureName) {
		TextureRegion region;
		if (regionsCache.containsKey(textureName)) {
			return new TextureRegion(regionsCache.get(textureName));
		}
		for (String atlas : loadedAtlasNames) {
			region = _loadTextureRegionFromAtlas(textureName, atlas);
			if (region != null) {
				regionsCache.put(textureName, region);
				return new TextureRegion(region);
			}
		}
		region = new TextureRegion(_loadGetTextureFromFile(textureName));
		regionsCache.put(textureName, region);
		return new TextureRegion(region);

	}

	private Texture _loadGetTextureFromFile(String textureName) {
		if (asset.isLoaded(textureName, Texture.class)) {
			return asset.get(textureName, Texture.class);
		} else {
			FileHandle fh = _getFile(textureName);
			if (!fh.exists()) {
				GM.logger.error(TAG, "No texture named [" + textureName + "] found!");
				return whiteRectTexture;
			} else {
				asset.load(textureName, Texture.class);
				asset.finishLoadingAsset(textureName);
				return asset.get(textureName, Texture.class);
			}
		}
	}

	private TextureRegion _loadTextureRegionFromAtlas(String textureName, String atlasName) {
		if (!asset.isLoaded(atlasName, TextureAtlas.class)) {
			asset.load(atlasName, TextureAtlas.class);
			asset.finishLoadingAsset(atlasName);
		}
		TextureAtlas ta = asset.get(atlasName, TextureAtlas.class);
		if (ta == null) {
			return null;
		}
		AtlasRegion ar = ta.findRegion(textureName);
		if (ar == null) {
			int p = textureName.lastIndexOf('.');
			ar = ta.findRegion(textureName.substring(0, p));
		}
		if (ar != null) {
			return ar;
		} else {
			return null;
		}
	}

	private Texture _getWhiteRect() {
		if (whiteRectTexture == null) {
			Pixmap tpm = new Pixmap(8, 8, Format.RGBA8888);
			tpm.setColor(Color.WHITE);
			tpm.fill();
			whiteRectTexture = new Texture(tpm);
			tpm.dispose();
		}
		return whiteRectTexture;
	}

	private Drawable _getMaskDrawable() {
		if (maskDrawable == null) {
			maskDrawable = new TextureRegionDrawable(new TextureRegion(_getWhiteRect()));
		}
		return maskDrawable;
	}

	private FileHandle _getFile(String filePath) {
		FileHandle fh = null;
		if (isGetFileCheckInternal) {
			fh = Gdx.files.internal(filePath);
			if (fh.exists()) {
				return fh;
			}
		}
		if (isGetFileCheckExternal) {
			fh = Gdx.files.external(filePath);
			if (fh.exists()) {
				return fh;
			}
		}
		if (isGetFileCheckResDirExternal) {
			fh = new FileHandle(RES_DIR + "/" + filePath);
			if (fh.exists()) {
				return fh;
			}
		}
		if (isGetFileCheckAbsolut) {
			fh = Gdx.files.absolute(filePath);
			if (fh.exists()) {
				return fh;
			}
		}
		return fh;
	}

	private void _dispose() {
		regionsCache.clear();
		loadedAtlasNames.clear();

		for (Disposable dp : disposables) {
			dp.dispose();
		}
		disposables.clear();

		for (Entry<String, Disposable> e : cachedDisposables.entrySet()) {
			if (e.getValue() != null) {
				e.getValue().dispose();
			}
		}
		cachedDisposables.clear();

		if (whiteRectTexture != null) {
			whiteRectTexture.dispose();
			whiteRectTexture = null;
		}
		if (asset != null) {
			asset.dispose();
			asset = null;
		}

		i = null;
	}

}
